package org.openlaszlo.util.service;

import java.io.File;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import samples.io.FileManager;
import samples.io.dto.BrowseResponseDTO;

/**
 * Utility servlet handling REST style requests, performing tasks on files.
 * This servlet could be used with any front-end, as long as required parameters
 * and URL style is prerequisites are met.
 * 
 * Currently the path required on the URL is hardcoded, this can be made configurable in the future.
 * URL's the servlet requires:
 * - <contextroot>/<servlet mapping>/delete
 * - <contextroot>/<servlet mapping>/<download or inline>/read
 * - <contextroot>/<servlet mapping>/move
 * - <contextroot>/<servlet mapping>/listdir
 * 
 * It also requires two standard parameters when performing a file base action, like read, delete or move:
 * - fileName (File name + extension)
 * - path (Full system path including file name)
 * 
 * For listing a directory, you'll have to provide the directory path to list:
 * - dir (The directory to list on the file system)
 * 
 * By default, the context root is 'fsweb' and the servlet mapping is 'fs'.
 * An example URL for a *nix type filesystem, would therefore be: 
 * http://localhost:8080/fsweb/fs/download/read?fileName=tmp.txt&path=/tmp/tmp.txt
 * 
 * @author Raoul Zon (XB Software Solutions BV, The Netherlands, http://www.xb.nl)
 */
public class FileHandlingServlet extends HttpServlet {

	private static final long serialVersionUID = -7606122669358524057L;
	private static String DELETE_FILE = "delete";
	private static String READ_FILE = "read";
	private static String MOVE_FILE = "move";
	private static String LIST_DIRECTORY = "listdir";
	
	private static int DOWNLOAD_ATTACHMENT = 0;
	private static int DOWNLOAD_INLINE = 1;
	
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		String requestUrl = request.getRequestURL().toString();
		String responseTxt = null;
		//just check part of URL for now, make this more fancy/configurable in the future
		String[] requestArray = requestUrl.split("/");
		String requestMethod = null;
		int downloadType = -1;
		if (requestArray.length == 6)
		{
			requestMethod = requestArray[5];
		}
		else if (requestArray.length == 7)
		{
			requestMethod = requestArray[6];
			if (requestArray[5].equals("download"))
				downloadType = DOWNLOAD_ATTACHMENT;
			else if (requestArray[5].equals("inline"))
				downloadType = DOWNLOAD_INLINE;
			
			if (downloadType == -1)
			{
				response.sendError(HttpServletResponse.SC_NOT_FOUND);
				return;
			}
		}
		
		OutputStream stream = response.getOutputStream();
		
		// is the request method correctly set, if not send error
		if (requestMethod == null || "".equals(requestMethod))
		{
			response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
			return;
		}
		else {
			String fileName = request.getParameter("fileName");
			String fullPath = request.getParameter("path");
			if (fileName == null || fullPath == null || 
					"".equals(fileName) || "".equals(fullPath))
			{
				response.sendError(HttpServletResponse.SC_NOT_ACCEPTABLE);
				return;
			}
			
			if (DELETE_FILE.equals(requestMethod)) {
				doFileDelete(fileName, fullPath);
			}
			else if (READ_FILE.equals(requestMethod)) {
				doFileRead(stream, fileName, fullPath, downloadType, response);
			}
			else if (MOVE_FILE.equals(requestMethod)) {
				doFileMove(fileName, fullPath, response);
			}
			else if (LIST_DIRECTORY.equals(requestMethod)) {
				String dirToRead = request.getParameter("dir");
				FileManager fm = new FileManager();
				BrowseResponseDTO brdto = fm.browse(dirToRead);
				if (brdto != null)
					responseTxt = brdto.toXML();

				if (responseTxt != null && !"".equals(responseTxt))
				{
					byte[] b = responseTxt.getBytes();
					stream.write(b);
					stream.flush();
					stream.close();
				}
			}
			else {
				response.sendError(HttpServletResponse.SC_BAD_REQUEST);
				return;
			}
		}
	}
	
	/**
	 * Performs a file deletion on the specified file
	 * @param fileName The name of the file to delete
	 * @param fullPath The complete path to the file including it's full name
	 * @param response The HttpServletResponse
	 */
	private void doFileDelete(String fileName, String fullPath)
	{
		File f = new File(fullPath);
		if (f.exists() && f.canWrite())
		{
			boolean deleted = f.delete();
			if (!deleted)
			{
				System.err.println("Could not delete file: "+fileName+"  ("+fullPath+")");
			}
		}
	}
	
	/**
	 * Reads the given file and streams it's content back to the requesting party
	 * @param stream The OutputStream to stream the contents to
	 * @param fileName The name of the file to stream
	 * @param fullPath The full path to the file, including it's name
	 * @param response The HttpServletResponse
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	private void doFileRead(OutputStream stream, 
			String fileName, 
			String fullPath, 
			int downloadType, 
			HttpServletResponse response)
		throws FileNotFoundException, IOException
	{	
		if (downloadType == DOWNLOAD_ATTACHMENT)
		{
    		response.setHeader("Content-Disposition","attachment; filename="+fileName);
    		response.setContentType("application/octet-stream");
		}
		
		// open file and write contents back to HTTP response 
		FileInputStream inFile = null;
		BufferedInputStream bis = null;
		try
		{
		    inFile = new FileInputStream(fullPath);
			bis = new BufferedInputStream(inFile);
			byte[] array = new byte[2048];
			int c = bis.read(array);
			while (c != -1)
			{
				stream.write(array,0,c);
				c = bis.read(array); 
			}
		}
		catch (Exception ex)
		{
			// most likely a ClientDisconnectException
			ex.printStackTrace();
			return;
		}
		finally {
		    if (bis != null)
		        bis.close();
			
		    if (stream != null)
		    {
    		    stream.flush();
    			stream.close();
		    }
			
		    if (inFile != null)
		        inFile.close();
		}
	}
	
	/**
	 * NOT IMPLEMENTED YET
	 * @param fileName
	 * @param fullPath
	 * @param response
	 */
	private void doFileMove(String fileName, String fullPath, HttpServletResponse response)
	{
		//TODO: Implement this feature
	}
}
